﻿using System;
using System.Collections.Generic;
using MugenMvvmToolkit.Infrastructure;
#if NETFX_CORE
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
#else
using System.Windows;
using System.Windows.Data;
#endif
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.ViewModels;

namespace MugenMvvmToolkit.Extensions
{
#if NODATACONTEXTCHANGED
#if !NETFX_CORE
    internal static class ViewModelDataContextChanged
    {
        private const string InternalContext = "ViewModelDataContextChanged";

        public static readonly DependencyProperty InternalDataContextProperty =
            DependencyProperty.Register(InternalContext,
                                        typeof(Object),
                                        typeof(ViewModelDataContextChanged),
                                        new PropertyMetadata(DataContextChanged));

        private static void DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            View.ViewModelOnDataContextChanged(sender, e);
        }

        public static void Bind(FrameworkElement control)
        {
            control.SetBinding(InternalDataContextProperty, new Binding());
        }
    }
#endif

    internal static class HasDesignDataContextChanged
    {
        private const string InternalContext = "HasDesignDataContextChanged";

        public static readonly DependencyProperty InternalDataContextProperty =
            DependencyProperty.Register(InternalContext,
                typeof(Object),
                typeof(HasDesignDataContextChanged),
                new PropertyMetadata(typeof(object), DataContextChanged));

        private static void DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            View.HasDesignOnDataContextChanged(sender, e.OldValue, e.NewValue);
        }

        public static void Bind(FrameworkElement control)
        {
            control.SetBinding(InternalDataContextProperty, new Binding());
        }

        public static void Unbind(FrameworkElement control)
        {
            control.ClearValue(InternalDataContextProperty);
        }
    }
#endif


    /// <summary>
    ///     Represents the extensions methods for view.
    /// </summary>
    public static class View
    {
        #region View extensions

        /// <summary>
        ///     Represents the attached property for a view model.
        /// </summary>
        public static readonly DependencyProperty HasDesignViewModelProperty =
            DependencyProperty.RegisterAttached("HasDesignViewModel", typeof(bool), typeof(View), new PropertyMetadata(default(bool), HasDesignViewModelChanged));

        /// <summary>
        ///     Sets a HasDesignViewModel property.
        /// </summary>
        public static void SetHasDesignViewModel(UIElement element, bool value)
        {
            element.SetValue(HasDesignViewModelProperty, value);
        }

        /// <summary>
        ///     Gets a HasDesignViewModel property.
        /// </summary>
        public static bool GetHasDesignViewModel(UIElement element)
        {
            return (bool)element.GetValue(HasDesignViewModelProperty);
        }

#if !NETFX_CORE
        /// <summary>
        ///     Represents the attached property for a view model.
        /// </summary>
        public static readonly DependencyProperty ViewModelProperty =
            DependencyProperty.RegisterAttached("ViewModel", typeof(object), typeof(View),
                                                new PropertyMetadata(default(object), ViewModelChanged));

        /// <summary>
        ///     Sets a view model
        /// </summary>
        public static void SetViewModel(UIElement uiElement, object value)
        {
            uiElement.SetValue(ViewModelProperty, value);
        }

        /// <summary>
        ///     Gets a view model.
        /// </summary>
        public static object GetViewModel(UIElement uiElement)
        {
            return uiElement.GetValue(ViewModelProperty);
        }
#endif

        #endregion

        #region Methods

        private static void HasDesignViewModelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            if (dependencyObject == null || !GlobalSettings.IsDesignMode)
                return;
            var frameworkElement = (FrameworkElement)dependencyObject;
            var newValue = (bool)dependencyPropertyChangedEventArgs.NewValue;
            if (newValue)
            {
                HasDesignOnDataContextChanged(dependencyObject, null, frameworkElement.DataContext);
#if NODATACONTEXTCHANGED
                HasDesignDataContextChanged.Bind(frameworkElement);
#else
                frameworkElement.DataContextChanged += HasDesignOnDataContextChanged;
#endif
                return;
            }
#if NODATACONTEXTCHANGED
            HasDesignDataContextChanged.Unbind(frameworkElement);
#else
            frameworkElement.DataContextChanged -= HasDesignOnDataContextChanged;
#endif
        }

#if !NODATACONTEXTCHANGED
        private static void HasDesignOnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            HasDesignOnDataContextChanged(sender, e.OldValue, e.NewValue);
        }
#endif
        internal static void HasDesignOnDataContextChanged(object sender, object oldValue, object newValue)
        {
            var frameworkElement = sender as FrameworkElement;
            if (frameworkElement == null || !GlobalSettings.IsDesignMode) return;

            var oldVm = oldValue as IViewModel;
            if (oldVm != null && oldVm.IsInitialized && oldVm != newValue)
                oldVm.Dispose();
            var newVm = newValue as IDesignViewModel;
            if (newVm == null) return;

            if (newVm.IsInitialized) return;
            newVm.InitializeViewModel();

// ReSharper disable once SuspiciousTypeConversion.Global
            var view = frameworkElement as IView;
            if (view == null) return;
            newVm.IocAdapter.Get<IViewManager>().InitializeView(newVm, view);
        }

#if !NETFX_CORE
        private static void ViewModelChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            if (dependencyObject == null)
                return;
            Should.BeView(dependencyObject.GetType(), "dependencyObject");
            var frameworkElement = (FrameworkElement)dependencyObject;
            BindingExpression bindingExpression = frameworkElement.GetBindingExpression(ViewModelProperty);
            if (bindingExpression == null) return;
            frameworkElement.ClearValue(ViewModelProperty);
#if NODATACONTEXTCHANGED
            ViewModelDataContextChanged.Bind(frameworkElement);
#else
            frameworkElement.DataContextChanged += ViewModelOnDataContextChanged;
#endif
            frameworkElement.SetBinding(FrameworkElement.DataContextProperty, bindingExpression.ParentBinding);
        }

        internal static void ViewModelOnDataContextChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            var frameworkElement = sender as FrameworkElement;
            if (frameworkElement == null) return;

            var newVm = args.NewValue as IViewModel;
// ReSharper disable once SuspiciousTypeConversion.Global
            if (newVm != null && newVm.IocAdapter != null)
                newVm.IocAdapter.Get<IViewManager>().InitializeView(newVm, (IView)frameworkElement);
        }
#endif
        #endregion
    }
}